﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Styx.CommonBot.Routines;
using Styx.Pathing;
using Styx;
using Styx.WoWInternals.WoWObjects;
using Styx.WoWInternals;
using Styx.CommonBot;
using System.Windows.Media;
using Styx.Common;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Styx.TreeSharp;
using Action = Styx.TreeSharp.Action;
using System.Diagnostics;
using System.Threading;
using Styx.WoWInternals.Misc;

namespace DestroLock
{
    public partial class Main : CombatRoutine
    {
        private static readonly Stopwatch WaitTimer = new Stopwatch();
        private static Stopwatch fightTimer = new Stopwatch();
        private static Stopwatch pullTimer = new Stopwatch();
        private static Stopwatch deadTimer = new Stopwatch();
        private static Stopwatch immolateTimer = new Stopwatch();
        private static ulong lastGuid = 0;

        public int CONFLAGRATE = 17962;
        public int NEW_RAIN_OF_FIRE = 104232;
        public int DARK_SOUL = 113858;
        private Random rnd = new Random();
        public string LastSpell;

        #region pet
        Composite CallMyPet()
        {
            return new Decorator(ret => (Me.Pet == null || !Me.GotAlivePet || Me.Pet.IsDead)
                    && !Me.Mounted
                    && !Me.IsFlying,
                new Action(ret =>
            {
                if (SpellManager.CanCast("Summon " + petName) && !Me.IsCasting && !Me.IsChanneling && LastSpell != "CallCombatPet")
                {
                    if (!petPresenceTimer.IsRunning)
                    {
                        petPresenceTimer.Restart();
                    }
                    if (petPresenceTimer.ElapsedMilliseconds >= 2000 && (Me.Pet == null || Me.Pet.IsDead))
                    {
                        petPresenceTimer.Reset();
                        SpellManager.Cast("Summon " + petName);
                        LogMsg("Summon " + petName, 0);
                        StyxWoW.SleepForLagDuration();
                    }
                }
                return RunStatus.Failure;
            }));
        }
        public string petName
        {
            get
            {
                string petNaam = LockSettings.myPrefs.useDemon.ToString();
                switch (petNaam)
                {
                    case "Imp":
                        if (SpellManager.HasSpell("Summon Fel Imp"))
                        {
                            return "Fel Imp";
                        }
                        return LockSettings.myPrefs.useDemon.ToString();
                    case "Voidwalker":
                        if (SpellManager.HasSpell("Summon Voidlord"))
                        {
                            return "Voidlord";
                        }
                        return LockSettings.myPrefs.useDemon.ToString();
                    case "Felhunter":
                        if (SpellManager.HasSpell("Summon Observer"))
                        {
                            return "Observer";
                        }
                        return LockSettings.myPrefs.useDemon.ToString();
                    case "Succubus":
                        if (SpellManager.HasSpell("Summon Shivarra"))
                        {
                            return "Shivarra";
                        }
                        return LockSettings.myPrefs.useDemon.ToString();
                    default:
                        return LockSettings.myPrefs.useDemon.ToString();
                }
            }
        }
        Composite takePetsTarget()
        {
            return new Decorator(ret => Me.CurrentTarget == null && (Me.PetInCombat || Me.PetAggro),
                new Action(ret =>
            {
                if (Me.Pet.CurrentTarget != null
                    && Me.CurrentTarget == null
                    && (Me.PetAggro || Me.PetInCombat))
                {
                    Me.Pet.CurrentTarget.Target();
                }
                return RunStatus.Failure;
            }));
        }
        #endregion pet

        #region between
        public bool myTargetBetween(float left, float right)
        {
            return Me.CurrentTarget.Distance >= left && Me.CurrentTarget.Distance <= right;
        }
        #endregion between

        #region AddCounting
        private int addCount
        {
            get
            {
                int count = 0;
                foreach (WoWUnit u in ObjectManager.GetObjectsOfType<WoWUnit>(true, true))
                {
                    if (gotTarget
                        && u.CanSelect
                        && !u.IsPlayer
                        && u.IsAlive
                        && u.Guid != Me.Guid
                        && !u.IsFriendly
                        && u.IsHostile
                        && u.Attackable
                        && !u.IsTotem
                        && !u.IsCritter
                        && !u.IsNonCombatPet
                        && (u.Location.Distance(Me.CurrentTarget.Location) <= 10 || u.Location.Distance2D(Me.CurrentTarget.Location) <= 10))
                    {
                        count++;
                    }
                }
                return count;
            }
        }
        #endregion AddCounting

        #region whoisattackingme

        public WoWUnit Attackers()
        {
            WoWUnit myAttacker = (from o in ObjectManager.ObjectList
                                  where o is WoWUnit
                                  let u = o.ToUnit()
                                  where u != null
                                  && u.IsTargetingMeOrPet
                                  && u.CanSelect
                                  orderby u.Location.Distance(Me.Location)
                                  select u)
                .FirstOrDefault();

            return myAttacker;
        }
        Composite whoIsAttackingMe()
        {
            return new Action(ret =>
            {
                WoWUnit newTarget = Attackers();
                if (Me.CurrentTarget != null && newTarget != Me.CurrentTarget)
                {
                    Me.ClearTarget();
                    LogMsg("Clearing my target and attck the one closest to me !", 5);
                    newTarget.Target();
                    LastSpell = "newTarget";
                }
                return RunStatus.Failure;
            });
        }
        #endregion whoisattackingme

        #region spellcasting
        private delegate T Selection<out T>(object context);
        Composite Cast(string spell, int kleur, Selection<bool> reqs = null)
        {
            return
                new Decorator(
                    ret => ((reqs != null && reqs(ret)) || (reqs == null) && (Me.CurrentTarget != null && !Me.CurrentTarget.IsDead)),
                    new Action(ret =>
                    {
                        if (SpellManager.CanCast(spell) && (!Me.IsCasting || !Me.IsChanneling))
                        {
                            SpellManager.Cast(spell, Me.CurrentTarget);
                            LogMsg(spell, kleur);
                            LastSpell = spell;
                        }
                        return RunStatus.Failure;
                    }));
        }
        Composite Cast(int spell, int kleur, Selection<bool> reqs = null)
        {
            return
                new Decorator(
                    ret => ((reqs != null && reqs(ret)) || (reqs == null) && (Me.CurrentTarget != null && !Me.CurrentTarget.IsDead)),
                    new Action(ret =>
                    {
                        if (SpellManager.CanCast(spell) && (!Me.IsCasting || !Me.IsChanneling))
                        {
                            SpellManager.Cast(spell, Me.CurrentTarget);

                            LastSpell = spell.ToString();
                            switch (LastSpell)
                            {
                                case "104232":
                                    LogMsg("Rain of Fire", kleur);
                                    break;
                                case "113858":
                                    LogMsg("Dark Soul", kleur);
                                    break;
                                default:
                                    LogMsg(spell.ToString(), kleur);
                                    break;
                            }
                        }
                        return RunStatus.Failure;
                    }));
        }
        Composite CastBuff(string spell, int kleur, Selection<bool> reqs = null)
        {
            return
                new Decorator(
                    ret => ((reqs != null && reqs(ret)) || (reqs == null) && (Me.CurrentTarget != null && !Me.CurrentTarget.IsDead)),
                    new Action(ret =>
                    {
                        if (SpellManager.CanCast(spell) && (!Me.IsCasting || !Me.IsChanneling))
                        {
                            SpellManager.Cast(spell, Me);
                            LogMsg(spell, kleur);
                            LastSpell = spell;
                        }
                        return RunStatus.Failure;
                    }));
        }
        Composite CastBuff(int spell, int kleur, Selection<bool> reqs = null)
        {
            return
                new Decorator(
                    ret => ((reqs != null && reqs(ret)) || (reqs == null) && (Me.CurrentTarget != null && !Me.CurrentTarget.IsDead)),
                    new Action(ret =>
                    {
                        if (SpellManager.CanCast(spell) && (!Me.IsCasting || !Me.IsChanneling))
                        {
                            SpellManager.Cast(spell, Me);

                            LastSpell = spell.ToString();
                            switch (LastSpell)
                            {
                                case "104232":
                                    LogMsg("Rain of Fire", kleur);
                                    break;
                                case "113858":
                                    LogMsg("Dark Soul", kleur);
                                    break;
                                default:
                                    LogMsg(spell.ToString(), kleur);
                                    break;
                            }
                        }
                        return RunStatus.Failure;
                    }));
        }
        Composite DropCast(string spell, int kleur)
        {
            return
                    new Action(ret =>
                    {
                        if ((!Me.IsCasting || !Me.IsChanneling) && (Me.CurrentTarget != null && !Me.CurrentTarget.IsDead))
                        {
                            SpellManager.Cast(spell);
                            StyxWoW.SleepForLagDuration();
                            SpellManager.ClickRemoteLocation(Me.CurrentTarget.Location);
                            StyxWoW.SleepForLagDuration();
                            LogMsg(spell, kleur);
                            LastSpell = spell;
                        }
                        return RunStatus.Failure;
                    });
        }
        Composite DropCast(int spell, int kleur)
        {
            return
                    new Action(ret =>
                    {
                        if ((!Me.IsCasting || !Me.IsChanneling) && (Me.CurrentTarget != null && !Me.CurrentTarget.IsDead))
                        {
                            SpellManager.Cast(spell);
                            StyxWoW.SleepForLagDuration();
                            SpellManager.ClickRemoteLocation(Me.CurrentTarget.Location);
                            StyxWoW.SleepForLagDuration();

                            LastSpell = spell.ToString();
                            switch (LastSpell)
                            {
                                case "104232":
                                    LogMsg("Rain of Fire", kleur);
                                    break;
                                case "113858":
                                    LogMsg("Dark Soul", kleur);
                                    break;
                                default:
                                    LogMsg(spell.ToString(), kleur);
                                    break;
                            }
                        }
                        return RunStatus.Failure;
                    });
        }
        #endregion spellcasting

        #region logs
        public void LogMsg(string msg, int kleurtje)
        {
            Color kleur = Colors.Yellow;
            switch (kleurtje)
            {
                case 0:
                    kleur = Colors.Orange;
                    break;
                case 1:
                    kleur = Colors.PaleGreen;
                    break;
                case 2:
                    kleur = Colors.BlanchedAlmond;
                    break;
                case 3:
                    kleur = Colors.Yellow;
                    break;
                case 4:
                    kleur = Colors.Red;
                    break;
                case 5:
                    kleur = Colors.Pink;
                    break;
                case 6:
                    kleur = Colors.LightBlue;
                    break;
                case 7:
                    kleur = Colors.Cyan;
                    break;
                case 8:
                    kleur = Colors.CornflowerBlue; // TotH
                    break;
                case 9:
                    kleur = Colors.Honeydew; //Lock and load
                    break;
                case 10:
                    kleur = Colors.DeepSkyBlue; //focusdump
                    break;
            }
            Logging.Write(kleur, msg);
        }
        #endregion logs

        #region inrange
        public bool IsInRange(float range, WoWUnit unit)
        {
            return unit != null && unit.Distance <= range;
        }
        #endregion inrage

        #region check pulltargets
        public Composite checkTargets()
        {
            return new Action(ret =>
                {
                    if (Battlegrounds.IsInsideBattleground)
                    {
                        if (Me.GotTarget &&
                            Me.CurrentTarget.IsPet)
                        {
                            Blacklist.Add(Me.CurrentTarget, TimeSpan.FromDays(1));
                            Me.ClearTarget();
                        }

                        if (Me.GotTarget && Me.CurrentTarget.Mounted)
                        {
                            Blacklist.Add(Me.CurrentTarget, TimeSpan.FromMinutes(1));
                            Me.ClearTarget();
                        }
                    }

                    if (Me.CurrentTarget.Guid != lastGuid)
                    {
                        fightTimer.Reset();
                        lastGuid = Me.CurrentTarget.Guid;
                        LogMsg("Killing " + Me.CurrentTarget.Name + " at distance " + System.Math.Round(Me.CurrentTarget.Distance).ToString() + ".", 6);
                        pullTimer.Reset();
                        pullTimer.Start();

                    }
                    else
                    {
                        if (pullTimer.ElapsedMilliseconds > 30 * 1000)
                        {
                            LogMsg("Cannot pull " + Me.CurrentTarget.Name + " now.  Blacklist for 3 minutes.", 4);
                            Styx.CommonBot.Blacklist.Add(Me.CurrentTarget.Guid, System.TimeSpan.FromMinutes(3));
                        }
                    }
                    return RunStatus.Failure;
                });
        }
        #endregion checkpulltargets

        #region bugged mobs
        public Composite checkBuggedMobs()
        {
            return new Decorator(ret => gotTarget,
                new Action(ret =>
                {
                    if (Me.GotTarget && (!fightTimer.IsRunning || Me.CurrentTarget.Guid != lastGuid))
                    {
                        fightTimer.Reset();
                        fightTimer.Start();
                        lastGuid = Me.CurrentTarget.Guid;
                    }
                    if (Me.CurrentTarget != null
                        && !Me.CurrentTarget.IsPlayer
                        && !IsWoWBoss(Me.CurrentTarget)
                        && fightTimer.ElapsedMilliseconds > 30 * 1000
                        && Me.CurrentTarget.HealthPercent > 95)
                    {
                        LogMsg(" This " + Me.CurrentTarget.Name + " is a bugged mob.  Blacklisting for 1 hour.", 4);

                        Blacklist.Add(Me.CurrentTarget.Guid, TimeSpan.FromHours(1.00));
                        Me.ClearTarget();
                        SafeMove(WoWMovement.MovementDirection.Backwards, 3000);
                        lastGuid = 0;
                    }
                    if (Me.CurrentTarget != null
                        && fightTimer.ElapsedMilliseconds > 30 * 1000
                        && !Me.Combat)
                    {
                        LogMsg("Take too long to engage " + Me.CurrentTarget.Name + " Blacklisting for 30 sec.", 5);
                        Blacklist.Add(Me.CurrentTarget.Guid, TimeSpan.FromSeconds(30.00));
                        Me.ClearTarget();
                        SafeMove(WoWMovement.MovementDirection.Backwards, 3000);
                        lastGuid = 0;
                    }
                    return RunStatus.Failure;
                }));
        }

        private void SafeMove(WoWMovement.MovementDirection direction, int duration)
        {
            DateTime start = DateTime.Now;

            WoWMovement.Move(direction);

            while (IsInGame && Me.HealthPercent > 1)
            {
                Thread.Sleep(335);

                if (DateTime.Now.Subtract(start).Milliseconds < 300 || DateTime.Now.Subtract(start).Milliseconds >= duration)
                {
                    break;
                }

            }

            WoWMovement.MoveStop(direction);
        }
        #endregion bugged mobs

        #region movement
        Composite createFacing()
        {
            return new Decorator(ret => gotTarget && !Me.IsSafelyFacing(Me.CurrentTarget) && LockSettings.myPrefs.Facing,
                new Action(ret =>
            {
                if (!Me.IsSafelyFacing(Me.CurrentTarget))
                {
                    Me.CurrentTarget.Face();
                }
                return RunStatus.Failure;
            }));
        }
        Composite createLignOfSight()
        {
            return new Decorator(ret => gotTarget && !Me.CurrentTarget.InLineOfSight && LockSettings.myPrefs.Movement,
                new Action(ret =>
            {
                LogMsg("Moving in lign of sight " + Me.CurrentTarget.Name, 1);
                while (!Me.CurrentTarget.InLineOfSight)
                {
                    Navigator.MoveTo(StyxWoW.Me.CurrentTarget.Location);
                }
                return RunStatus.Failure;
            }));
        }
        Composite createMoveInRange(float range)
        {
            return new Decorator(ret => gotTarget && Me.CurrentTarget.Distance > 39 && LockSettings.myPrefs.Movement,
                new Action(ret =>
            {
                LogMsg("Moving in range " + range + " " + Me.CurrentTarget.Name, 1);
                while (Me.CurrentTarget.Distance > range)
                {
                    Navigator.MoveTo(StyxWoW.Me.CurrentTarget.Location);
                }
                return RunStatus.Failure;
            }));
        }
        Composite createStopMove()
        {
            return new Decorator(ret => gotTarget && Me.CurrentTarget.Distance <= 35 && LockSettings.myPrefs.Movement,
                new Action(ret =>
            {
                if (Me.CurrentTarget.Distance <= 35)
                {
                    Navigator.PlayerMover.MoveStop();
                }
                return RunStatus.Failure;
            }));
        }
        public Composite MoveControlls(float range)
        {

            return new Decorator(ret => gotTarget && LockSettings.myPrefs.Movement,
                new Action(ret =>
                {
                    if (Me.CurrentTarget.Distance > range && LockSettings.myPrefs.Movement)
                    {
                        LogMsg("Moving in range " + range + " " + Me.CurrentTarget.Name, 1);
                        while (Me.CurrentTarget != null && Me.CurrentTarget.Distance > range)
                        {
                            Navigator.MoveTo(StyxWoW.Me.CurrentTarget.Location);
                        }
                    }
                    if (!Me.CurrentTarget.InLineOfSight && LockSettings.myPrefs.Movement)
                    {
                        LogMsg("Moving in lign of sight " + Me.CurrentTarget.Name, 1);
                        while (Me.CurrentTarget != null && !Me.CurrentTarget.InLineOfSight)
                        {
                            Navigator.MoveTo(StyxWoW.Me.CurrentTarget.Location);
                        }
                    }
                    if (Me.CurrentTarget.InLineOfSight && !Me.CurrentTarget.InLineOfSpellSight && LockSettings.myPrefs.Movement)
                    {
                        LogMsg("Moving in lign of spell sight " + Me.CurrentTarget.Name, 1);
                        while (Me.CurrentTarget != null && Me.CurrentTarget.InLineOfSight && !Me.CurrentTarget.InLineOfSpellSight)
                        {
                            Navigator.MoveTo(StyxWoW.Me.CurrentTarget.Location);
                        }
                    }
                    if (!Me.IsSafelyFacing(Me.CurrentTarget) && LockSettings.myPrefs.Facing)
                    {
                        Me.CurrentTarget.Face();
                    }
                    if (LockSettings.myPrefs.Movement && Me.IsMoving)
                    {
                        Navigator.PlayerMover.MoveStop();
                    }
                    return RunStatus.Failure;
                }));
        }
        #endregion movement

        #region trinket1
        public Composite useTrinket1()
        {
            return new Decorator(ret => gotTarget
                && (LockSettings.myPrefs.useTrinket1 == LockSettings.CooldowPrefs.OnCoolDown
                || LockSettings.myPrefs.useTrinket1 == LockSettings.CooldowPrefs.Bosses && IsWoWBoss(Me.CurrentTarget))
                && LastSpell != "Trinket1" && LastSpell != "Trinket2",
                new Action(ret =>
                {
                    var firstTrinket = StyxWoW.Me.Inventory.Equipped.Trinket1;

                    if (firstTrinket != null && CanUseEquippedItem(firstTrinket))
                    {
                        firstTrinket.Use();
                        LogMsg("Using 1st trinket", 8);
                        LastSpell = "Trinket1";
                    }
                    return RunStatus.Failure;
                }));
        }
        #endregion trinket1

        #region trinket2
        public Composite useTrinket2()
        {
            return new Decorator(ret => gotTarget
                && (LockSettings.myPrefs.useTrinket2 == LockSettings.CooldowPrefs.OnCoolDown
                || LockSettings.myPrefs.useTrinket2 == LockSettings.CooldowPrefs.Bosses && IsWoWBoss(Me.CurrentTarget))
                && LastSpell != "Trinket1" && LastSpell != "Trinket2",
                new Action(ret =>
                {
                    var secondTrinket = StyxWoW.Me.Inventory.Equipped.Trinket2;

                    if (secondTrinket != null && CanUseEquippedItem(secondTrinket))
                    {
                        secondTrinket.Use();
                        LogMsg("Using 2nd trinket", 8);
                        LastSpell = "Trinket2";
                    }
                    return RunStatus.Failure;
                }));
        }
        #endregion trinket2

        #region engineer gloves
        public Composite useEngiGloves()
        {
            return new Decorator(ret => gotTarget 
                && (LockSettings.myPrefs.useEngiGloves == LockSettings.CooldowPrefs.OnCoolDown
                || LockSettings.myPrefs.useEngiGloves == LockSettings.CooldowPrefs.Bosses && IsWoWBoss(Me.CurrentTarget))
                && LastSpell != "Gloves",
                new Action(ret =>
                {
                    var engiGloves = StyxWoW.Me.Inventory.Equipped.Hands;

                    if (engiGloves != null && CanUseEquippedItem(engiGloves))
                    {
                        engiGloves.Use();
                        LogMsg("Using Engineer Gloves", 8);
                        LastSpell = "Gloves";
                    }
                    return RunStatus.Failure;
                }));
        }
        #endregion engineer gloves

        #region healthstone
        public Composite useHealthStone()
        {
            return new Decorator(ret => gotTarget && Me.HealthPercent <= LockSettings.myPrefs.healthstonepercent && LastSpell != "Healthstone",
                new Action(ret =>
                {
                    WoWItem hstone = Me.BagItems.FirstOrDefault(h => h.Entry == 5512);

                    if (hstone != null && hstone.CooldownTimeLeft.TotalMilliseconds <= 0)
                    {
                        hstone.Use();
                        Logging.Write(Colors.BlanchedAlmond, "Using Healthstone");
                        LastSpell = "HealthStone";
                    }

                    return RunStatus.Failure;
                }));
        }
        public Composite createHealthStone()
        {
            return new Decorator(ret => SpellManager.HasSpell("Create Healthstone")
                    && LockSettings.myPrefs.createHealthStone
                    && !Me.Mounted
                    && !Me.IsFlying
                    && !Me.IsMoving
                    && LastSpell != "Create Healthstone"
                    && !Me.IsCasting,
                new Action(ret =>
            {
                WoWItem hstone = Me.BagItems.FirstOrDefault(h => h.Entry == 5512);
                if (hstone == null && LastSpell != "Create Healthstone")
                {
                    SpellManager.Cast("Create Healthstone");
                    LogMsg("Creating Healtstone", 0);
                    LastSpell = "Create Healthstone";
                }
                return RunStatus.Failure;
            }));
        }
        #endregion healthstone

        #region can use equipped items
        private static bool CanUseEquippedItem(WoWItem item)
        {
            string itemSpell = Lua.GetReturnVal<string>("return GetItemSpell(" + item.Entry + ")", 0);
            if (string.IsNullOrEmpty(itemSpell))
            {
                return false;
            }
            return item.Usable && item.Cooldown <= 0;
        }
        #endregion can use equipped items

        #region Buff Checks

        public bool buffExists(int Buff, WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                var Results = onTarget.GetAuraById(Buff);
                if (Results != null)
                    return true;
            }
            return false;
        }

        public double buffTimeLeft(int Buff, WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                var Results = onTarget.GetAuraById(Buff);
                if (Results != null)
                {
                    if (Results.TimeLeft.TotalMilliseconds > 0)
                        return Results.TimeLeft.TotalMilliseconds;
                }
            }
            return 0;
        }

        public bool buffExists(string Buff, WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                var Results = onTarget.GetAuraByName(Buff);
                if (Results != null)
                    return true;
            }
            return false;
        }

        public double buffTimeLeft(string Buff, WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                var Results = onTarget.GetAuraByName(Buff);
                if (Results != null)
                {
                    if (Results.TimeLeft.TotalMilliseconds > 0)
                        return Results.TimeLeft.TotalMilliseconds;
                }
            }
            return 0;
        }



        public uint buffStackCount(int Buff, WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                var Results = onTarget.GetAuraById(Buff);
                if (Results != null)
                    return Results.StackCount;
            }
            return 0;
        }
        public uint buffStackCount(string Buff, WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                var Results = onTarget.GetAuraByName(Buff);
                if (Results != null)
                    return Results.StackCount;
            }
            return 0;
        }
        #endregion

        #region Cache Checks
        public IEnumerable<WoWAura> cachedAuras = new List<WoWAura>();
        public IEnumerable<WoWAura> cachedTargetAuras = new List<WoWAura>();
        public void getCachedAuras()
        {
            if (Me.CurrentTarget != null)
                cachedTargetAuras = Me.CurrentTarget.GetAllAuras();
            cachedAuras = Me.GetAllAuras();
        }
        #endregion

        #region Cooldown Checks
        private Dictionary<WoWSpell, long> Cooldowns = new Dictionary<WoWSpell, long>();
        public TimeSpan cooldownLeft(int Spell)
        {
            SpellFindResults Results;
            if (SpellManager.FindSpell(Spell, out Results))
            {
                WoWSpell Result = Results.Override ?? Results.Original;
                if (Cooldowns.TryGetValue(Result, out lastUsed))
                {
                    if (DateTime.Now.Ticks < lastUsed)
                        return Result.CooldownTimeLeft;
                    return TimeSpan.MaxValue;
                }
            }
            return TimeSpan.MaxValue;
        }
        public TimeSpan cooldownLeft(string Spell)
        {
            SpellFindResults Results;
            if (SpellManager.FindSpell(Spell, out Results))
            {
                WoWSpell Result = Results.Override ?? Results.Original;
                if (Cooldowns.TryGetValue(Result, out lastUsed))
                {
                    if (DateTime.Now.Ticks < lastUsed)
                        return Result.CooldownTimeLeft;
                    return TimeSpan.MaxValue;
                }
            }
            return TimeSpan.MaxValue;
        }
        long lastUsed;
        public int lastCast;
        public bool onCooldown(int Spell)
        {
            SpellFindResults Results;
            if (SpellManager.FindSpell(Spell, out Results))
            {
                WoWSpell Result = Results.Override ?? Results.Original;
                if (Cooldowns.TryGetValue(Result, out lastUsed))
                {
                    if (DateTime.Now.Ticks < lastUsed)
                        return Result.Cooldown;
                    return false;
                }
            }
            return false;
        }
        public bool onCooldown(string Spell)
        {
            SpellFindResults Results;
            if (SpellManager.FindSpell(Spell, out Results))
            {
                WoWSpell Result = Results.Override ?? Results.Original;
                if (Cooldowns.TryGetValue(Result, out lastUsed))
                {
                    if (DateTime.Now.Ticks < lastUsed)
                        return Result.Cooldown;
                    return false;
                }
            }
            return false;
        }
        public TimeSpan spellCooldownLeft(int Spell, bool useTracker = false)
        {
            if (useTracker)
                return cooldownLeft(Spell);
            SpellFindResults results;
            if (SpellManager.FindSpell(Spell, out results))
            {
                if (results.Override != null)
                    return results.Override.CooldownTimeLeft;
                return results.Override.CooldownTimeLeft;
            }
            return TimeSpan.MaxValue;
        }
        public TimeSpan spellCooldownLeft(string Spell, bool useTracker = false)
        {
            if (useTracker)
                return cooldownLeft(Spell);
            SpellFindResults results;
            if (SpellManager.FindSpell(Spell, out results))
            {
                if (results.Override != null)
                    return results.Override.CooldownTimeLeft;
                return results.Override.CooldownTimeLeft;
            }
            return TimeSpan.MaxValue;
        }
        public bool spellOnCooldown(int Spell, bool useTracker = false)
        {
            if (useTracker)
                return !onCooldown(Spell);
            SpellFindResults results;
            if (SpellManager.FindSpell(Spell, out results))
                return results.Override != null ? results.Override.Cooldown : results.Original.Cooldown;
            return false;
        }
        public bool spellOnCooldown(string Spell, bool useTracker = false)
        {
            if (useTracker)
                return !onCooldown(Spell);
            SpellFindResults results;
            if (SpellManager.FindSpell(Spell, out results))
                return results.Override != null ? results.Override.Cooldown : results.Original.Cooldown;
            return false;
        }
        #endregion

        #region Debuff Checks

        public bool debuffExists(int Debuff, WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                WoWAura aura = onTarget.GetAllAuras().FirstOrDefault(a => a.SpellId == Debuff && a.CreatorGuid == Me.Guid);
                if (aura != null)
                {
                    return true;
                }
            }
            return false;
        }

        public double debuffTimeLeft(int Debuff, WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                WoWAura aura = onTarget.GetAllAuras().FirstOrDefault(a =>
                    a.SpellId == Debuff
                    && a.CreatorGuid == Me.Guid);

                if (aura == null)
                {
                    return 0;
                }
                return aura.TimeLeft.TotalMilliseconds;
            }
            return 0;
        }

        public uint debuffStackCount(int Debuff, WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                WoWAura aura = onTarget.GetAllAuras().FirstOrDefault(a =>
                    a.SpellId == Debuff
                    && a.CreatorGuid == Me.Guid);

                if (aura == null)
                {
                    return 0;
                }
                return aura.StackCount;
            }
            return 0;
        }
        public bool debuffExists(string Debuff, WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                WoWAura aura = onTarget.GetAllAuras().FirstOrDefault(a => a.Name == Debuff && a.CreatorGuid == Me.Guid);
                if (aura != null)
                {
                    return true;
                }
            }
            return false;
        }

        public double debuffTimeLeft(string Debuff, WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                WoWAura aura = onTarget.GetAllAuras().FirstOrDefault(a =>
                    a.Name == Debuff
                    && a.CreatorGuid == Me.Guid);

                if (aura == null)
                {
                    return 0;
                }
                return aura.TimeLeft.TotalMilliseconds;
            }
            return 0;
        }

        public uint debuffStackCount(string Debuff, WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                WoWAura aura = onTarget.GetAllAuras().FirstOrDefault(a =>
                    a.Name == Debuff
                    && a.CreatorGuid == Me.Guid);

                if (aura == null)
                {
                    return 0;
                }
                return aura.StackCount;
            }
            return 0;
        }
        #endregion

        #region Target Checks

        internal IEnumerable<WoWUnit> attackableTargets
        {
            get { return ObjectManager.GetObjectsOfType<WoWUnit>(true, false).Where(U => U.Attackable && U.CanSelect && !U.IsFriendly && !U.IsDead && !U.IsNonCombatPet && !U.IsCritter); }
        }
        public bool gotTarget { get { return Me.GotTarget && Me.CurrentTarget != null && Me.CurrentTarget.Attackable && (!Me.CurrentTarget.IsDead && !Me.CurrentTarget.IsFriendly); } }

        

        internal IEnumerable<WoWUnit> nearbyTargets(WoWPoint fromLocation, double Radius)
        {
            var Hostile = attackableTargets;
            var maxDistance = Radius * Radius;
            return Hostile.Where(x => x.Location.DistanceSqr(fromLocation) < maxDistance);
        }
        public double targetDistance(WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                var Results = onTarget.DistanceSqr;
                if (Results != null)
                    return onTarget.DistanceSqr;
            }
            return 0;
        }
        public double targetHP(WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                var Results = onTarget.HealthPercent;
                if (Results != null)
                    return onTarget.HealthPercent;
            }
            return 0;
        }
        #endregion

        #region Timer Checks
        private int conv_Date2Timestam(DateTime _time)
        {
            var date1 = new DateTime(1970, 1, 1);
            DateTime date2 = _time;
            var ts = new TimeSpan(date2.Ticks - date1.Ticks);
            return (Convert.ToInt32(ts.TotalSeconds));
        }
        private uint current_life;
        private int current_time;
        private ulong guid;
        private uint first_life;
        private uint first_life_max;
        private int first_time;
        public long targetExistence(WoWUnit onTarget)
        {
            if (onTarget == null) return 0;
            if (Me.CurrentTarget.Name.Contains("Dummy")) return 9999;
            if (onTarget.CurrentHealth == 0 || onTarget.IsDead || !onTarget.IsValid || !onTarget.IsAlive)
                return 0;
            if (guid != onTarget.Guid)
            {
                guid = onTarget.Guid;
                first_life = onTarget.CurrentHealth;
                first_life_max = onTarget.MaxHealth;
                first_time = conv_Date2Timestam(DateTime.Now);
            }
            current_life = onTarget.CurrentHealth;
            current_time = conv_Date2Timestam(DateTime.Now);
            var time_diff = current_time - first_time;
            var hp_diff = first_life - current_life;
            if (hp_diff > 0)
            {
                var full_time = time_diff * first_life_max / hp_diff;
                var past_first_time = (first_life_max - first_life) * time_diff / hp_diff;
                var calc_time = first_time - past_first_time + full_time - current_time;
                if (calc_time < 1) calc_time = 99;
                var time_to_die = calc_time;
                var fight_length = full_time;
                return time_to_die;
            }
            if (hp_diff < 0)
            {
                guid = onTarget.Guid;
                first_life = onTarget.CurrentHealth;
                first_life_max = onTarget.MaxHealth;
                first_time = conv_Date2Timestam(DateTime.Now);
                return -1;
            }
            if (current_life == first_life_max)
                return 9999;
            return -1;
        }
        #endregion

        #region havoc
        public WoWUnit getHavocTargets()
        {

            WoWUnit havocTarget = (from o in ObjectManager.ObjectList
                                   where o is WoWUnit
                                   let unit = o.ToUnit()
                                   where unit != Me.CurrentTarget
                                   && unit.MaxHealth >= Me.MaxHealth
                                   && Me.IsSafelyFacing(unit)
                                   && !debuffExists("Havoc", unit)
                                   && !unit.IsPet
                                   && !unit.IsTotem
                                   && (unit.IsTargetingMeOrPet || unit.IsTargetingMyPartyMember || unit.IsTargetingMyRaidMember)
                                   orderby unit.HealthPercent descending
                                   select unit)
                                   .FirstOrDefault();
            if (havocTarget != null)
            {
                return havocTarget;
            }
            return null;

        }
        Composite castHavoc()
        {
            return new Decorator(ret => gotTarget
                    && !buffExists("Havoc", Me)
                    && LastSpell != "Havoc",
                new Action(ret =>
            {
                WoWUnit havocTarget = getHavocTargets();
                if (havocTarget != null && SpellManager.CanCast("Havoc") && !spellOnCooldown("Havoc"))
                {
                    SpellManager.Cast("Havoc", havocTarget);
                    LogMsg("Casting Havoc on : " + havocTarget.Name, 4);
                    LastSpell = "Havoc";
                }
                return RunStatus.Failure;
            }));
        }
        #endregion havoc

        #region ImmolateDot
        public List<WoWUnit> NearbyUnFriendlyUnits = new List<WoWUnit>();
        //public WoWUnit UnitToDotImmo;

        public void GetDotUnits()
        {
            NearbyUnFriendlyUnits.Clear();

            IEnumerable<WoWUnit> units =
                ObjectManager.GetObjectsOfType<WoWUnit>(true, true).Where(
                    unit => !unit.IsTotem
                            && !debuffExists("Immolate", unit)
                            && (unit.IsTargetingMeOrPet || unit.IsTargetingMyPartyMember || unit.IsTargetingMyRaidMember)
                            && Me.IsSafelyFacing(unit)
                            && !Blacklist.Contains(unit.Guid, BlacklistFlags.All));

            foreach (WoWUnit unit in units)
            {
              NearbyUnFriendlyUnits.Add(unit);
            }
        }
        public Composite MultiDotImmo()
        {
            return new Decorator(ret => gotTarget && addCount > 1 && (!immolateTimer.IsRunning || immolateTimer.ElapsedMilliseconds >= 15 * 1000),
                new Action(ret =>
                    {
                        immolateTimer.Reset();

                        GetDotUnits();

                        int maxUnits = NearbyUnFriendlyUnits.Count() -1;
                        if (maxUnits > LockSettings.myPrefs.maxDotTargets)
                        {
                            maxUnits = LockSettings.myPrefs.maxDotTargets -1;
                        }

                        for(int i = 0; i <= maxUnits; i++)  
                            {
                                WoWUnit targetToDot = NearbyUnFriendlyUnits[i];
                                SpellManager.Cast("Immolate", targetToDot);
                                StyxWoW.SleepForLagDuration();
                                LogMsg("[MultiDot] Immolate " + targetToDot.Name, 2);
                                LastSpell = "ImmolateDot";
                            }
                        immolateTimer.Restart();
                        return RunStatus.Failure;
                    }));
        }
        #endregion ImmolateDot

        #region Kind of target
        public bool IsWoWBoss(WoWUnit mytarget)
        {

            if (gotTarget
                && Me.CurrentTarget.CreatureRank == WoWUnitClassificationType.WorldBoss)
            {
                return true;
            }
            else if (gotTarget
                && target.IsBoss(mytarget))
            {
                return true;
            }
            else if (gotTarget
                && Me.CurrentTarget.MaxHealth >= (65 * 1000000))
            {
                return true;
            }
            else if (gotTarget
                    && Me.CurrentTarget.CreatureRank == WoWUnitClassificationType.RareElite
                    && Me.CurrentTarget.MaxHealth >= (Me.MaxHealth * LockSettings.myPrefs.ElitesHealth)
                    && !Me.IsInInstance
                    && !Me.GroupInfo.IsInRaid)
            {
                return true;
            }
            else if (gotTarget
                     && Me.CurrentTarget.CreatureRank == WoWUnitClassificationType.Rare
                     && Me.CurrentTarget.MaxHealth >= (Me.MaxHealth * LockSettings.myPrefs.ElitesHealth)
                     && !Me.IsInInstance
                     && !Me.GroupInfo.IsInRaid)
            {
                return true;
            }
            else if (gotTarget
                && Me.CurrentTarget.CreatureRank == WoWUnitClassificationType.Elite
                && Me.CurrentTarget.MaxHealth >= (Me.MaxHealth * LockSettings.myPrefs.ElitesHealth)
                && !Me.IsInInstance
                && !Me.GroupInfo.IsInRaid)
            {
                return true;
            }
            else if (gotTarget
                && Me.CurrentTarget.Name.Contains("Training Dummy")
                && !Me.IsInInstance)
            {
                return true;
            }
            else if (gotTarget
                && Me.CurrentTarget.IsPlayer
                && !Me.CurrentTarget.IsFriendly)
            {
                return true;
            }
            return false;
        }

        #endregion Kind of Target

        #region summon infernal
        public string myInfernal
        {
            get { if (SpellManager.HasSpell("Summon Abyssal")) { return "Summon Abyssal"; } return "Summon Infernal"; }
        }
        public bool SummonMyInfernal()
        {
            return (LockSettings.myPrefs.useSummonInfernal == LockSettings.SummonInfernal.Five_and_More_Adds && addCount >= 5
                || (LockSettings.myPrefs.useSummonInfernal == LockSettings.SummonInfernal.Ten_and_More_Adds && addCount >= 10)
                || (LockSettings.myPrefs.useSummonInfernal == LockSettings.SummonInfernal.Bosses_and_Adds && IsWoWBoss(Me.CurrentTarget) && addCount > 3)
                || (LockSettings.myPrefs.useSummonInfernal == LockSettings.SummonInfernal.Bosses_Only && IsWoWBoss(Me.CurrentTarget)));
        }
        #endregion summon infernal

        #region darksoul
        public bool useDarksSoul
        {
            get
            {
                return (LockSettings.myPrefs.darkSoul == LockSettings.CooldowPrefs.OnCoolDown
                || (LockSettings.myPrefs.darkSoul == LockSettings.CooldowPrefs.Bosses && IsWoWBoss(Me.CurrentTarget)));
            }
        }
        #endregion darksoul

        #region summon doomguard
        public string doomGuard
        {
            get { if (SpellManager.HasSpell("Summon Terrorguard")) { return "Summon Terrorguard"; } return "Summon Doomguard"; }
        }
        public bool SummonMyDoomGuard()
        {
            return (LockSettings.myPrefs.useSummonDoomGuard == LockSettings.CooldowPrefs.OnCoolDown
                || (LockSettings.myPrefs.useSummonDoomGuard == LockSettings.CooldowPrefs.Bosses && IsWoWBoss(Me.CurrentTarget)));
        }
        #endregion summon doomguard

        #region increaseStatsPotions
        public int increaseStatsPotions()
        {
            string selectedPotion = LockSettings.myPrefs.useStatsPotion.ToString();
            switch (selectedPotion)
            {
                case "Virmens_Bite":
                    return 76089;
                case "Potion_of_the_Mountains":
                    return 76090;
                case "Potion_of_Focus":
                    return 76092;
                case "Potion_of_the_Jade_Serpent":
                    return 76093;
                case "Potion_of_Mogu_Power":
                    return 76095;
                default:
                    return 0;
            }
        }

        public Composite useIncreaseStatsPotion()
        {
            return new Decorator(ret => gotTarget
                && (LockSettings.myPrefs.whenToUseStatsPotion == LockSettings.WhenUseStatsPotion.AllBosses && IsWoWBoss(Me.CurrentTarget))
                || (LockSettings.myPrefs.whenToUseStatsPotion == LockSettings.WhenUseStatsPotion.RaidBosses && Me.GroupInfo.IsInRaid && IsWoWBoss(Me.CurrentTarget)),
                new Action(ret =>
                {
                    int kindOfpotion = increaseStatsPotions();
                    WoWItem potion = Me.BagItems.FirstOrDefault(h => h.Entry == kindOfpotion);

                    if (potion != null && LastSpell != "UseStatsPotion" && potion.CooldownTimeLeft.TotalMilliseconds <= 0)
                    {
                        potion.Use();
                        Logging.Write(Colors.BlanchedAlmond, "Using " + potion.Name);
                        LastSpell = "UseStatsPotion";
                    }
                    return RunStatus.Failure;
                }));
        }
        #endregion increaseStatsPotions

        #region flask
        public Composite useMyFlask()
        {
            return new Decorator(ret => gotTarget && !buffExists(FlaskBuff, Me)
                && ((LockSettings.myPrefs.whenToUseFlask == LockSettings.WhenToUseFlask.Instances && Me.IsInInstance)
                || (LockSettings.myPrefs.whenToUseFlask == LockSettings.WhenToUseFlask.Raids && Me.GroupInfo.IsInRaid)
                || (LockSettings.myPrefs.whenToUseFlask == LockSettings.WhenToUseFlask.Raids_but_not_LFR && Me.GroupInfo.IsInRaid && !Me.GroupInfo.IsInLfgParty)),
                new Action(ret =>
                {
                    int myFlask = myFlaskToUse();
                    WoWItem flask = Me.BagItems.FirstOrDefault(h => h.Entry == myFlask);

                    if (flask != null && LastSpell != "UseFlask" && flask.CooldownTimeLeft.TotalMilliseconds <= 0)
                    {
                        flask.Use();
                        Logging.Write(Colors.BlanchedAlmond, "Using " + flask.Name);
                        LastSpell = "UseFlask";
                    }
                    return RunStatus.Failure;
                }));
        }
        public int myFlaskToUse()
        {
            string myFlask = LockSettings.myPrefs.kindOfFlaskToUse.ToString();
            switch (myFlask)
            {
                case "Strenght":
                    return 76088;
                case "Stamina":
                    return 76087;
                case "Spirit":
                    return 76086;
                case "Intellect":
                    return 76085;
                case "Agility":
                    return 76084;
                default:
                    return 0;
            }
        }
        public int FlaskBuff
        {
            get { return myFlaskToUse(); }
        }
        #endregion flask

        #region alchemy flask
        public Composite useAlchemyFlask()
        {
            return new Decorator(ret => !aclchemyBuff() && !buffExists(FlaskBuff, Me),
                new Action(ret =>
            {
                WoWItem alchflask = Me.BagItems.FirstOrDefault(h => h.Entry == 75525);

                if (alchflask != null && LastSpell != "UseAlchemy")
                {
                    alchflask.Use();
                    Logging.Write(Colors.LightBlue, "Using Alchemist's Flask");
                    LastSpell = "UseAlchemy";
                }
                return RunStatus.Failure;
            }));
        }
        public bool aclchemyBuff()
        {
            return buffExists("Enhanced Agility", Me) || buffExists("Enhanced Intellect", Me) || buffExists("Enhanced Strenght", Me) || buffExists("Visions of Insanity", Me);
        }
        #endregion alchemy flask

        #region crystal of insanity
        public Composite useCrystalofInsanity()
        {
            return new Decorator(ret => gotTarget && !buffExists(FlaskBuff, Me) && !buffExists("Visions of Insanity", Me) && LastSpell != "MakeCrystal",
                new Action(ret =>
            {
                WoWItem crystal = StyxWoW.Me.BagItems.FirstOrDefault(h => h.Entry == 86569);

                if (crystal != null && LastSpell != "MakeCrystal" && crystal.CooldownTimeLeft.TotalMilliseconds <= 0)
                {
                    crystal.Use();
                    Logging.Write(Colors.BlanchedAlmond, "Using Crystal of Insanity");
                    LastSpell = "MakeCrystal";
                }
                return RunStatus.Failure;
            }));
        }
        #endregion crystal of insanity

        #region curse
        public string castCurse()
        {
            string curseSpell = LockSettings.myPrefs.useCurse.ToString();
            switch (curseSpell)
            {
                case "Corruption":
                    return "Corruption";
                case "Curse_of_Enfleebement":
                    return "Curse of Enfleebement";
                case "Curse_of_the_Elements":
                    return "Curse of the Elements";
                default:
                    return "None";
            }
        }
        public bool haveCurse()
        {
            return SpellManager.HasSpell(castCurse());
        }
        #endregion curse

        #region create soulstone
        Composite createSoulstone()
        {
            return new Decorator(ret => SpellManager.HasSpell("Soulstone")
                    && !spellOnCooldown("Soulstone")
                    && !Me.Mounted
                    && !Me.IsFlying
                    && !Me.IsCasting,
            new Action(ret =>
            {
                if (LockSettings.myPrefs.createSoulstone == LockSettings.SoulstonePrefs.onMySelf
                    && !buffExists("Soulstone", Me)
                    && LastSpell != "CreateSoulstone"
                    && !Me.IsCasting)
                {
                    SpellManager.Cast("Soulstone", Me);
                    LogMsg("Creating Soulstone on myself", 2);
                    LastSpell = "CreateSoulstone";
                }
                if (LockSettings.myPrefs.createSoulstone == LockSettings.SoulstonePrefs.onFocus
                    && Me.FocusedUnit != null
                    && !buffExists("Soulstone", Me.FocusedUnit)
                    && LastSpell != "CreateSoulstone"
                    && !Me.IsCasting)
                {
                    SpellManager.Cast("Soulstone", Me.FocusedUnit);
                    LogMsg("Creating Soulstone " + Me.FocusedUnit.Name, 2);
                    LastSpell = "CreateSoulstone";
                }
                return RunStatus.Failure;
            }));
        }
        #endregion create soulstone

        #region ensureTarget
        Composite ensureTarget()
        {
            return new Decorator(ret => Me.CurrentTarget == null && LockSettings.myPrefs.Targeting,
            new Action(ret =>
            {
                GetTargets.EnsureTarget();
            }));
        }
        #endregion ensureTarget

        #region howl of terror
        Composite HowlOfTerror()
        {
            return new Action(ret =>
            {
                if (SpellManager.CanCast("Howl of Terror"))
                {
                    Me.ClearTarget();
                    SpellManager.Cast("Howl of Terror");
                    LogMsg("Howl of Terror", 3);
                    LastSpell = "Howl of Terror";
                    GetTargets.EnsureTarget();
                }
                return RunStatus.Failure;
            });
        }
        #endregion howl of terror

        #region spellcharges
        public int HaveSpellCharges
        {
            get
            {
                return Lua.GetReturnVal<int>("return GetSpellCharges(17962)", 0);
            }
        }
        #endregion spellcharges

        #region dismount when questing
        Composite disMount()
        {
            return new Decorator(ret => Me.Mounted && !Me.IsFlying && LockSettings.myPrefs.Movement,
                new Action(ret =>
                {
                    Lua.DoString("Dismount()");
                    LogMsg("Dismounting cause we are attacked", 4);
                    return RunStatus.Failure;
                }));
        }
        #endregion dismount when questing

        #region cleartarget
        Composite clearMyTarget()
        {
            return new Decorator(ret => gotTarget && Me.Combat,
                new Action(ret =>
                {
                    if (Me.CurrentTarget.IsFriendly || Me.CurrentTarget.IsDead)
                    {
                        Me.ClearTarget();
                    }
                    return RunStatus.Failure;
                }));
        }
        #endregion cleartarget

    }
}
